package com.firemoss.ui.blanket.util
{
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import mx.core.UIComponent;
	
	/**
	 * Enumerates orientation options and provides default implementations
	 * of positioning.
	 * <p>
	 * Creating multiple instances allows creation of different orientation
	 * strategies, one instance per strategy.</p>
	 * <p>
	 * For each instance, you can set the default orientation and relative
	 * offsets.</p>
	 * <p>
	 * Setting the static "defaultOrientationPriority" variable
	 * sets the default for any Orientation instance that does not
	 * have its own default.
	 * </p>
	 */
	public class Orientation
	{
		
		// STATIC
		
		public static const TOP_RIGHT : int = 0
		public static const TOP_CENTER : int = 1
		public static const TOP_LEFT : int = 2
		public static const MIDDLE_RIGHT : int = 3
		// middle center is just obnoxious and useless for callouts... public static const MIDDLE_CENTER : int = 4
		public static const MIDDLE_LEFT : int = 4
		public static const BOTTOM_RIGHT : int = 5
		public static const BOTTOM_CENTER : int = 6
		public static const BOTTOM_LEFT : int = 7
		
		public static var defaultOrientationPriority : Array = [
			TOP_RIGHT,
			TOP_CENTER,
			TOP_LEFT,
			MIDDLE_RIGHT,
			MIDDLE_LEFT,
			BOTTOM_RIGHT,
			BOTTOM_CENTER,
			BOTTOM_LEFT
		]
		public static var defaultRelativeX : Number = 0
		public static var defaultRelativeY : Number = 0
		
		// PUBLIC PROPERTIES
		
		/**
		 * For this instance, what's the default priority for orientation?  
		 */
		public var defaultOrientationPriority : Array = Orientation.defaultOrientationPriority
		
		/**
		 * For this instance, what's the default relative offset for X?
		 * <p>
		 * It's relative to orientation direction:  moves right in RIGHT orientations,
		 * nothing in CENTER, left in LEFT.
		 */
		public var defaultRelativeX : Number = Orientation.defaultRelativeX
		
		/**
		 * For this instance, what's the default relative offset for Y?
		 * <p>
		 * It's relative to orientation direction:  moves up in TOP orientations,
		 * nothing in MIDDLE, down in BOTTOM.
		 */
		public var defaultRelativeY : Number = Orientation.defaultRelativeY
		
		// PUBLIC METHODS
		
		public function getOrientedPosition( source : UIComponent, target : UIComponent, relativeX : Number = NaN, relativeY : Number  = NaN) : OrientationResult
		{
			if ( isNaN( relativeX ) )
			{
				relativeX = defaultRelativeX	
			}
			if ( isNaN( relativeY ) )
			{
				relativeY = defaultRelativeY
			}
			
			// Gives us bounds for checking position
			var screen : Rectangle = target.screen
			
			// Is the display of what we're about to show off-screen?
			var offScreen : Boolean = false
			var noPriorityFits : Boolean = false
			var pos : int = -1
			var orientation : int
			var newX : int
			var newY : int
			var targetTopLeft : Point = target.localToGlobal( new Point( target.x, target.y ) )
			var firstResult : OrientationResult
			var validResult : OrientationResult
			
			// Account for local position
			
			targetTopLeft.x -= target.x
			targetTopLeft.y -= target.y
			
			/* Determine which orientation to use:  go through the priority, finding what'll remain
			 * on-screen.  If none, use first.
			 */
			
			var tw : Number = target.getExplicitOrMeasuredWidth()
			
			while ( !firstResult || (offScreen && !noPriorityFits) )
			{
				pos++
				orientation = defaultOrientationPriority[ pos ]
				
				switch ( orientation )
				{
					case TOP_RIGHT:
						newX = targetTopLeft.x + target.width + relativeX
						newY = targetTopLeft.y - source.getExplicitOrMeasuredHeight() - relativeY
					break;
					case TOP_CENTER:
						newX = targetTopLeft.x + target.width / 2 - source.getExplicitOrMeasuredWidth() / 2
						newY = targetTopLeft.y - source.getExplicitOrMeasuredHeight() - relativeY
					break;
					case TOP_LEFT:
						newX = targetTopLeft.x - relativeX - source.getExplicitOrMeasuredWidth()
						newY = targetTopLeft.y - source.getExplicitOrMeasuredHeight() - relativeY
					break;
					case MIDDLE_RIGHT:
						newX = targetTopLeft.x + target.width + relativeX
						newY = targetTopLeft.y  + target.height / 2 - source.getExplicitOrMeasuredHeight() / 2
					break;
					/*
					case MIDDLE_CENTER:
						newX = targetTopLeft.x + target.width / 2 - source.width / 2
						newY = targetTopLeft.y + target.height / 2
						trace("middleCenter")
					break;
					*/
					case MIDDLE_LEFT:
						newX = targetTopLeft.x - relativeX - source.getExplicitOrMeasuredWidth()
						newY = targetTopLeft.y  + target.height / 2 - source.getExplicitOrMeasuredHeight() / 2
					break;
					case BOTTOM_RIGHT:
						newX = targetTopLeft.x + target.width + relativeX
						newY = targetTopLeft.y + target.height + relativeY
					break;
					case BOTTOM_CENTER:
						newX = targetTopLeft.x + target.width / 2 - source..getExplicitOrMeasuredWidth() / 2
						newY = targetTopLeft.y + target.height + relativeY
					break;
					case BOTTOM_LEFT:
						newX = targetTopLeft.x - relativeX - source.getExplicitOrMeasuredWidth()
						newY = targetTopLeft.y + target.height + relativeY
					break;
				}

				// Broken into multiple tests for ease of debugging
				if (newX < 0)
				{
					offScreen = true
				}
				else if ( newY < 0 )
				{
					offScreen = true
				}
				else if ( newX + source.width > target.screen.width )
				{
					offScreen = true
				}
				else if ( newY + source.height > target.screen.height )
				{
					offScreen = true
				}
				else
				{
					offScreen = false
				}
				
				if ( !firstResult )
				{
					firstResult = new OrientationResult()
					firstResult.position = new Point( newX, newY )
					firstResult.orientation = orientation
				}	
						
				if ( !offScreen )
				{
					validResult = new OrientationResult()
					validResult.position = new Point( newX, newY )
					validResult.orientation = orientation
				}
					
				// If we've tried all, revert to first
				if ( offScreen && pos == defaultOrientationPriority.length - 1 )
				{
					noPriorityFits = true
					validResult = firstResult
				}
			}
			
			return validResult
		} 
		
	}
}